package com.southgis.ibase.sql.builder;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.util.CollectionUtils;

import com.southgis.ibase.sql.builder.SqlBuilder.SelectSqlBuilder;
import com.southgis.ibase.sql.enums.FuncDefine;
import com.southgis.ibase.sql.exception.ParamFormatException;
import com.southgis.ibase.sql.utils.ConvertUtil;
import com.southgis.ibase.sql.utils.DatabaseFunc;
import com.southgis.ibase.sql.utils.SqlUtil;

/**
 * 子句内容项（比如：字段、条件）的定义基类，子类包括：字段信息、常量、函数调用、计算式、逻辑表达式等。<br>
 * 其中，字段信息、常量、函数调用、计算式，又称“单项值”子句
 * @author dnnyo
 *
 */
public abstract class ClauseItem
{

	DatabaseFunc func;
	public void setDatabaseFunc(DatabaseFunc func)
	{
		this.func=func;
	}

	public abstract void toSql(StringBuilder sb);
	
	void objectTo(Object val, StringBuilder sb)
	{
		if(val==null) {
			sb.append(" null ");
		}
		else if(val instanceof ClauseItem) {
			ClauseItem temp=(ClauseItem)val;
			temp.func=this.func;
			temp.toSql(sb);
		}else if(val instanceof SelectSqlBuilder) {
			SelectSqlBuilder temp=(SelectSqlBuilder)val;
			temp.func=this.func;
			sb.append(temp.build());
		}else {
			SingleValue temp=ClauseBuilder.valueToCond(val);
			temp.func=this.func;
			temp.toSql(sb);
		}
	}
	//================================
	
	//README 单项值
	/**
	 * 单项值的条件项，是其他如“常量”、“函数调用”、“计算式”等的基类
	 * @author dnnyo
	 *
	 */
	static class SingleValue extends ClauseItem
	{
		Object value;
		
		SingleValue(Object value)
		{
			this.value=value;
		}
		
		Object getValue()
		{
			return value;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			sb.append(value);
		}
	}
	
	/**
	 * 查询参数(? 或 ?n 或 :name)
	 * @author dnnyo
	 *
	 */
	static class ParamValue extends SingleValue
	{
		ParamValue(String value)
		{
			super(value);
			
			char chx=value.charAt(0);
			String fmtInfo="? 或 ?n 或 :name（n为数值，name为字母数字及_）";
			if(chx=='?') {
				for(int ix=1;ix<value.length();++ix) {
					if(!Character.isDigit(value.charAt(ix))) {
						throw new ParamFormatException(value, fmtInfo);
					}
				}
			}else if(chx==':') {
				for(int ix=1;ix<value.length();++ix) {
					chx=value.charAt(ix);
					if(!Character.isLetterOrDigit(chx) && chx!='_') {
						throw new ParamFormatException(value, fmtInfo);
					}
				}
			}else {
				throw new ParamFormatException(value, fmtInfo);
			}
			//this.value=value;
		}
		
//		@Override
//		public void toSql(StringBuilder sb)
//		{
//			sb.append(value);
//		}
	}
	
	/**
	 * 常量值。字符串值不用引号包含，在转sql语句时，会自动处理
	 * @author dnnyo
	 *
	 */
	static class ConstantValue extends SingleValue
	{
		/**
		 * 字符串值不用引号包含，在转sql语句时，会自动处理
		 * @param value
		 */
		ConstantValue(Object value)
		{
			super(value);
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			if(value==null) {
				sb.append(" null");
				return;
			}
			
			boolean istext=(value instanceof String);
			if(istext) {
				sb.append("'");
				sb.append(SqlUtil.toSql((String)value));
				sb.append("'");
			}else {
				sb.append(value);
			}
		}
	}
	
	/**
	 * 集合类型的值，支持SelectSqlBuilder、Object[]、Collection
	 * @author dnnyo
	 *
	 */
	static class CollectionValue extends SingleValue
	{
		CollectionValue(Object value)
		{
			super(value);
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			boolean first=true;
			sb.append("(");
			if(value==null) {
				sb.append("null");
			} else if(value instanceof SelectSqlBuilder) {
				SelectSqlBuilder tbl=(SelectSqlBuilder)value;
				tbl.databaseFunc(func);
				sb.append(tbl.build());
			}else if(value instanceof Collection) {
				@SuppressWarnings("unchecked")
				Collection<Object> temp=(Collection<Object>)value;
				boolean str=false;
				for(Object it:temp) {
					if(!first) sb.append(",");
					else {
						first=false;
						str=(it instanceof String);
					}
					if(str) {
						sb.append("'");
						sb.append(SqlUtil.toSql((String)it));
						sb.append("'");
					}else {
						sb.append(it);
					}
				}
			}else if(value.getClass().isArray()) {
				if(value instanceof String[]) {
					for(String it:(String[])value) {
						if(!first) sb.append(",");
						else first=false;
						sb.append("'");
						sb.append(SqlUtil.toSql(it));
						sb.append("'");
					}
				}else {
					boolean str=false;
					for(Object it:(Object[])value) {
						if(!first) sb.append(",");
						else {
							first=false;
							str=(it instanceof String);
						}
						if(str) {
							sb.append("'");
							sb.append(SqlUtil.toSql((String)it));
							sb.append("'");
						}else {
							sb.append(it);
						}
					}
				}
			}else if(value instanceof String) {
				sb.append("'");
				sb.append(SqlUtil.toSql((String)value));
				sb.append("'");
			}else {
				sb.append(value);
			}
			sb.append(")");
		}
	}
	/**
	 * 字段
	 * @author dnnyo
	 *
	 */
	static class FieldValue extends SingleValue
	{
		String alias;
		FieldValue(TableColumn value)
		{
			super(value);
			alias=value.getAlias();
		}
		
		FieldValue(Object value, String alias)
		{
			super(value);
			this.alias=alias;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			if(value instanceof TableColumn)
				((TableColumn)value).toSql(1, sb);
			else {
				if(!StringUtils.isEmpty(alias))
					sb.append("(");
				objectTo(value, sb);
				if(!StringUtils.isEmpty(alias)) {
					sb.append(") as ").append(alias);
				}
			}
		}
	}
	/**
	 * case转换表达式
	 * @author dnnyo
	 *
	 */
	static class CaseValue extends SingleValue
	{
		/**
		 * when-then子句
		 */
		List<MutablePair<Object, Object>> caseWhen;
		Object caseElse;
		
		/**
		 * case转换表达式，以value与when值比较，相等则返回then值
		 * @param value
		 * @param caseWhen
		 * @param caseElse
		 */
		CaseValue(TableColumn value, List<MutablePair<Object, Object>> caseWhen, Object caseElse)
		{
			super(value);

			this.caseWhen=caseWhen;
			this.caseElse=caseElse;
		}
		
		/**
		 * case转换表达式，判断when值为true时，返回then值
		 * @param alias
		 * @param caseWhen
		 * @param caseElse
		 */
		CaseValue(List<MutablePair<Object, Object>> caseWhen, Object caseElse)
		{
			super(null);

			this.caseWhen=caseWhen;
			this.caseElse=caseElse;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			SingleValue sv;
			
			sb.append("(CASE ");
			if(CollectionUtils.isEmpty(caseWhen)) {
				//when col is not null then col
				sb.append("WHEN ");
				if(value!=null) {
					TableColumn col=(TableColumn)value;
					col.toSql(SqlBuilder.BUILD_SECTION_CONDITION, sb);
					sb.append(" is not null THEN ");
					col.toSql(SqlBuilder.BUILD_SECTION_CONDITION, sb);
				}else {
					sb.append(" false THEN null");
				}
			} else {
				if(value!=null)
					((TableColumn)value).toSql(SqlBuilder.BUILD_SECTION_CONDITION, sb);
				
				for(Pair<Object, Object> p:caseWhen) {
					sb.append(" WHEN ");
					if(p.getKey() instanceof ClauseItem) {
						((ClauseItem)p.getKey()).toSql(sb);
					}else {
						sv=ClauseBuilder.valueToCond(p.getKey());
						sv.toSql(sb);
					}
					sb.append(" THEN ");
					sv=ClauseBuilder.valueToCond(p.getValue());
					sv.toSql(sb);
					sb.append("\n");
				}
			}
			
			if(caseElse!=null) {
				sb.append(" ELSE ");
				sv=ClauseBuilder.valueToCond(caseElse);
				sv.func=this.func;
				sv.toSql(sb);
			}
			
			sb.append(" END)");
		}
	}
	
	/**
	 * 函数调用。内部会对定义在FuncDefine中的函数进行处理。：lower-转小写、upper-转大写、ifnull-null则用另一个值、<br>
	 * concat-字符串拼接、substr-取子串、todate(fn,type)-字符串转日期、year-取年份值、now-当前时间
	 * @author dnnyo
	 *
	 */
	static class FunctionValue extends SingleValue
	{
		Object[] param;
		FunctionValue(String name, Object... params)
		{
			super(name);
			this.param=params;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			final String token="'?0'";
			if(func==null)
				func=DatabaseFunc.getInstance();
			String commName=(String)value;
			
			if(commName.equalsIgnoreCase(FuncDefine.NOW)) {//now-当前时间
				sb.append(func.getNowInvoke());
			}else if(commName.equalsIgnoreCase(FuncDefine.CONCAT)) {//concat-字符串拼接
				String[] sparams=new String[param.length];
				int ix;
				for(ix=0;ix<sparams.length;++ix)
				{
					sparams[ix]=token;
				}
				String invoke=func.concatChar(sparams);
				//List<String> lsparams=Splitter.on(token).splitToList(invoke);
				//sb.append(lsparams.get(0));
				sparams=StringUtils.splitByWholeSeparatorPreserveAllTokens(invoke, token);
				sb.append(sparams[0]);
				ix=1;
				for(Object v:param)
				{
					objectTo(v,sb);
					sb.append(sparams[ix++]);
					//sb.append(lsparams.get(ix++));
				}
			}else if(commName.equalsIgnoreCase(FuncDefine.CAST)) {
				//需要两个参数：转换的值，转换后的类型
				String invoke=func.getCastInvoke(token, ConvertUtil.getValue(param[1],0), 0, 0);
				int pos=invoke.indexOf(token);
				sb.append(invoke.substring(0,pos));
				objectTo(param[0],sb);
				sb.append(invoke.substring(pos+token.length()));
			} else {
				if(!toKnownFunc((String)value, sb)) {
					toDatabaseFunc((String)value, sb);
				}
				
			}
		}
		/**
		 * 处理已知的函数
		 * @param commName
		 * @param sb
		 * @return
		 */
		protected boolean toKnownFunc(String commName, StringBuilder sb)
		{
			final String token="'?0'";
			String funcName="";
			if(commName.equalsIgnoreCase(FuncDefine.IFNULL)) {
				funcName=func.getIfnullInvoke(token,token);
			}else if(commName.equalsIgnoreCase(FuncDefine.LOWER)) {
				funcName=func.getLowerInvoke(token);
			}else if(commName.equalsIgnoreCase(FuncDefine.UPPER)) {
				funcName=func.getUpperInvoke(token);
			}else if(commName.equalsIgnoreCase(FuncDefine.TODATE)) {
				funcName=func.getCharToDateInvoke(token, ConvertUtil.getValue(param[1],0));
			}else if(commName.equalsIgnoreCase(FuncDefine.YEAR)) {
				funcName=func.getYearInvoke(token);
			}else if(commName.equalsIgnoreCase(FuncDefine.SUBSTR)) {
				funcName=func.subChar(token,ConvertUtil.getValue(param[1],0),ConvertUtil.getValue(param[2],0));
			}
			
			boolean knownFunc=!funcName.isEmpty();
			if(knownFunc) {
				//List<String> lsparams=Splitter.on(token).splitToList(funcName);
				String[] sparams=StringUtils.splitByWholeSeparatorPreserveAllTokens(funcName, token);

				//sb.append(lsparams.get(0));
				sb.append(sparams[0]);
				for(int ix=1;ix<sparams.length;ix++)
				{
					objectTo(param[ix-1],sb);
					sb.append(sparams[ix]);
					//sb.append(lsparams.get(ix));
				}
			}
			return knownFunc;
		}
		
		/**
		 * 通用处理，将方法调用转为sql输出（未考虑跨库的兼容性）
		 * @param commName
		 * @param sb
		 * @return
		 */
		protected boolean toDatabaseFunc(String commName, StringBuilder sb)
		{
			sb.append(commName).append("(");
			if(param!=null) {
				boolean first=true;
				for(Object v:param)
				{
					if(!first) sb.append(",");
					else first=false;
					
					objectTo(v,sb);
				}
			}
			sb.append(")");
			
			return true;
		}
	}
	
	/**
	 * 计算表达式，比如：+、-、*、/。
	 * 因为很少用在sql中，暂不考虑更丰富的功能。
	 * @author dnnyo
	 *
	 */
	static class CalculationValue extends SingleValue
	{
		SingleValue left;
		SingleValue right;
		
		/**
		 * 生成计算的条件项 左值和右值必须是SingleValue
		 * @param left
		 * @param opt
		 * @param right
		 */
		CalculationValue(ClauseItem left, String opt, ClauseItem right)
		{
			super(opt);
			this.left=(SingleValue)left;
			this.right=(SingleValue)right;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			left.func=this.func;
			right.func=this.func;
			left.toSql(sb);
			sb.append(" ").append(value).append(" ");
			right.toSql(sb);
		}

	}
	
	/**
	 * 用于分段拼接的值，各分段以空格分隔
	 * @author dnnyo
	 *
	 */
	static class SegmentValue extends SingleValue
	{
		List<Object> segValue=new ArrayList<>();
		SegmentValue()
		{
			super("");
		}
		
		/**
		 * 添加一个分段
		 * @param item
		 */
		public SegmentValue add(Object item)
		{
			segValue.add(item);
			return this;
		}
		
		/**
		 * 添加一个用括号包含的分段。返回值与原对象不同，是一个新分段对象，表示括号内的内容。<br>
		 * 且返回的新分段只需添加自己的内容即可，不需要再重复添加到原分段中。如：
		 * <pre>
		 * seg.addBracketBlock("ROW_NUMBER"); //因为没有内容，所以调用即可，不需要考虑返回对象
		 * SegmentValue block=seg.addBracketBlock("Over"); //需要为分段添加内容，所以获取返回值
		 * block.add("PARTITION BY").add("fld");
		 * 
		 * seg.toSql(); //ROW_NUMBER() Over(PARTITION BY fld)
		 * </pre>
		 * @param item
		 * @return
		 */
		public SegmentValue addBracketBlock(Object item)
		{
			SegmentValue block=new SegmentValue();
			
			segValue.add(item);
			segValue.add("(");
			segValue.add(block);
			segValue.add(")");
			
			return block;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			boolean first=true;
			for(Object sv:segValue) {
				if(!first) {
					//修正一些空格
					if(!(sv instanceof String) && !(sv instanceof SegmentValue))
						sb.append(" ");
					else if(sv instanceof String) {
						String temp=(String)sv;
						if(!temp.isEmpty() && Character.isLetterOrDigit(temp.charAt(0)))
							sb.append(" ");
					}
				}
				else first=false;
				
				if(sv instanceof TableColumn) {
					((TableColumn)sv).toSql(SqlBuilder.BUILD_SECTION_CONDITION, sb);
				}else if(sv instanceof ClauseItem) {
					ClauseItem item=(ClauseItem)sv;
					item.func=this.func;
					item.toSql(sb);
				}else if(sv instanceof SelectSqlBuilder) {
					SelectSqlBuilder sql=(SelectSqlBuilder)sv;
					sql.func=this.func;
					sb.append(sql.build());
				} else if(sv instanceof SegmentValue){
					((SegmentValue)sv).func=this.func;
					((SegmentValue)sv).toSql(sb);
				}else {
					sb.append(sv);//片段表达式，原样输出
				}
			}
		}
	}
	
	//README 逻辑判断条件项
	
	/**
	 * 所有逻辑条件的基类
	 * @author dnnyo
	 *
	 */
	static abstract class BoolCond extends ClauseItem
	{
		String optor;
		
		BoolCond(String optor)
		{
			this.optor=optor;
		}
		
		void toSql(StringBuilder sb, ClauseItem[] nodes)
		{
			boolean first=true;
			for(ClauseItem it:nodes) {
				if(!first) sb.append(optor);
				else first=false;
				sb.append(" (");
				it.func=this.func;
				it.toSql(sb);
				sb.append(")");
			}
		}
	}
	
	/**
	 * 条件判断表达式
	 * @author dnnyo
	 *
	 */
	static class ExpressionCond extends BoolCond
	{
		SingleValue left;
		SingleValue right;
		
		/**
		 * 生成表达式的条件项 左值和右值必须是SingleValue
		 * @param left
		 * @param optor 比较操作符
		 * @param right
		 */
		ExpressionCond(ClauseItem left, String optor, ClauseItem right)
		{
			super(optor);
			this.left=(SingleValue)left;
			this.right=(SingleValue)right;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			if(left!=null) {
				left.func=this.func;
				left.toSql(sb);
			}
			sb.append(optor);
			if(right!=null) {
				right.func=this.func;
				right.toSql(sb);
			}else {
				sb.append("null");
			}
		}

	}
	
	/**
	 * not条件
	 * @author dnnyo
	 *
	 */
	static class NotCond extends BoolCond
	{
		ClauseItem org;

		NotCond(ClauseItem value)
		{
			super(" not ");
			this.org=value;
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			sb.append(optor).append("(");
			org.func=this.func;
			org.toSql(sb);
			sb.append(")");
		}
		
	}
	/**
	 * and条件
	 * @author dnnyo
	 *
	 */
	static class AndCond extends BoolCond
	{
		ClauseItem[] nodes;
		AndCond(ClauseItem... nodes)
		{
			super(" and ");
			this.nodes=nodes;
		}
		AndCond(List<ClauseItem> nodes)
		{
			super(" and ");
			this.nodes=new ClauseItem[nodes.size()];
			nodes.toArray(this.nodes);
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			toSql(sb, nodes);
		}
	}
	/**
	 * or条件
	 * @author dnnyo
	 *
	 */
	static class OrCond extends BoolCond
	{
		ClauseItem[] nodes;
		OrCond(ClauseItem... nodes)
		{
			super(" or ");
			this.nodes=nodes;
		}
		OrCond(List<ClauseItem> nodes)
		{
			super(" or ");
			this.nodes=new ClauseItem[nodes.size()];
			nodes.toArray(this.nodes);
		}
		
		@Override
		public void toSql(StringBuilder sb)
		{
			toSql(sb, nodes);
		}
	}
}
